bounded_type.hpp
namespace type_safe
{
namespace constraints
{
struct dynamic_bound;
template <typename T, typename Bound = dynamic_bound>
class less;
template <typename T, typename Bound = dynamic_bound>
class less_equal;
template <typename T, typename Bound = dynamic_bound>
class greater;
template <typename T, typename Bound = dynamic_bound>
class greater_equal;
//=== Open/Closed Tags ===//
constexpr bool open = false;
constexpr bool closed = true;
template <typename T, bool LowerInclusive, bool UpperInclusive, typename LowerBound = dynamic_bound, typename UpperBound = dynamic_bound>
class bounded;
template <typename T, typename LowerBound = dynamic_bound, typename UpperBound = dynamic_bound>
using open_interval = bounded<T, open, open, LowerBound, UpperBound>;
template <typename T, typename LowerBound = dynamic_bound, typename UpperBound = dynamic_bound>
using closed_interval = bounded<T, closed, closed, LowerBound, UpperBound>;
}
template <typename T, bool LowerInclusive, bool UpperInclusive, typename LowerBound = constraints::dynamic_bound, typename UpperBound = constraints::dynamic_bound, typename Verifier = assertion_verifier>
using bounded_type = constrained_type<T, constraints::bounded<T, LowerInclusive, UpperInclusive, LowerBound, UpperBound>, Verifier>;
inline namespace literals
{
template <char ... Digits>
constexpr 'hidden' operator""_bound();
template <char ... Digits>
constexpr 'hidden' operator""_boundu();
}
template <typename T, typename U1, typename U2>
constexpr 'hidden' make_bounded(T&& value, U1&& lower, U2&& upper);
template <typename T, typename U1, typename U2>
constexpr 'hidden' sanitize_bounded(T&& value, U1&& lower, U2&& upper);
template <typename T, typename U1, typename U2>
constexpr 'hidden' make_bounded_exclusive(T&& value, U1&& lower, U2&& upper);
template <typename T, typename U1, typename U2>
constexpr 'hidden' sanitize_bounded_exclusive(T&& value, U1&& lower, U2&& upper);
//=== clamped_type ===//
template <typename T, typename LowerBound, typename UpperBound, typename U>
constexpr typename std::decay<U>::type clamp(const constraints::closed_interval<T, LowerBound, UpperBound>& interval, U&& val);
struct clamping_verifier;
template <typename T, typename LowerBound = constraints::dynamic_bound, typename UpperBound = constraints::dynamic_bound>
using clamped_type = constrained_type<T, constraints::closed_interval<T, LowerBound, UpperBound>, clamping_verifier>;
template <typename T, typename U1, typename U2>
constexpr 'hidden' make_clamped(T&& value, U1&& lower, U2&& upper);
}
type_safe::constraints::dynamic_bound
struct dynamic_bound
{
};
Tag type to enable a dynamic bound.
type_safe::constraints::less
template <typename T, typename Bound = dynamic_bound>
class less
{
public:
using value_type = decltype(base::value);
using bound_type = Bound;
constexpr less(Bound = {});
constexpr less(const T& bound);
constexpr less(T&& bound) noexcept('hidden');
template <typename U>
constexpr bool operator()(const U& u) const;
constexpr const value_type& get_bound() const noexcept;
};
A Constraint
for the ts::constrained_type.
A value is valid if it is less than some given value.
type_safe::constraints::less::less::less
constexpr less(Bound = {});
Initializes it with a static bound.
Effects: Does nothing, a static bound is not stored. It will use Bound::value
as the bound.
Notes: This constructor only participates in overload resolution, if a static bound is used, i.e. Bound
is not ts::constraints::dynamic_bound.
type_safe::constraints::less::less::less
(1) constexpr less(const T& bound);
(2) constexpr less(T&& bound) noexcept('hidden');
Initializes it with a dynamic bound.
Effects: Copies (1)/moves (2) the object and uses that as bound.
Notes: These constructors only participate in overload resolution, if a dynamic bound is used, i.e. Bound
is ts::constraints::dynamic_bound.
type_safe::constraints::less::operator()
template <typename U>
constexpr bool operator()(const U& u) const;
Does the actual bounds check.
type_safe::constraints::less::get_bound
constexpr const value_type& get_bound() const noexcept;
Returns: The bound.
type_safe::constraints::less_equal
template <typename T, typename Bound = dynamic_bound>
class less_equal
{
public:
using value_type = decltype(base::value);
using bound_type = Bound;
constexpr less_equal(Bound = {});
constexpr less_equal(const T& bound);
constexpr less_equal(T&& bound) noexcept('hidden');
template <typename U>
constexpr bool operator()(const U& u) const;
constexpr const value_type& get_bound() const noexcept;
};
A Constraint
for the ts::constrained_type.
A value is valid if it is less than or equal to some given value.
type_safe::constraints::less_equal::less_equal::less_equal
constexpr less_equal(Bound = {});
Initializes it with a static bound.
Effects: Does nothing, a static bound is not stored. It will use Bound::value
as the bound.
Notes: This constructor only participates in overload resolution, if a static bound is used, i.e. Bound
is not ts::constraints::dynamic_bound.
type_safe::constraints::less_equal::less_equal::less_equal
(1) constexpr less_equal(const T& bound);
(2) constexpr less_equal(T&& bound) noexcept('hidden');
Initializes it with a dynamic bound.
Effects: Copies (1)/moves (2) the object and uses that as bound.
Notes: These constructors only participate in overload resolution, if a dynamic bound is used, i.e. Bound
is ts::constraints::dynamic_bound.
type_safe::constraints::less_equal::operator()
template <typename U>
constexpr bool operator()(const U& u) const;
Does the actual bounds check.
type_safe::constraints::less_equal::get_bound
constexpr const value_type& get_bound() const noexcept;
Returns: The bound.
type_safe::constraints::greater
template <typename T, typename Bound = dynamic_bound>
class greater
{
public:
using value_type = decltype(base::value);
using bound_type = Bound;
constexpr greater(Bound = {});
constexpr greater(const T& bound);
constexpr greater(T&& bound) noexcept('hidden');
template <typename U>
constexpr bool operator()(const U& u) const;
constexpr const value_type& get_bound() const noexcept;
};
A Constraint
for the ts::constrained_type.
A value is valid if it is greater than some given value.
type_safe::constraints::greater::greater::greater
constexpr greater(Bound = {});
Initializes it with a static bound.
Effects: Does nothing, a static bound is not stored. It will use Bound::value
as the bound.
Notes: This constructor only participates in overload resolution, if a static bound is used, i.e. Bound
is not ts::constraints::dynamic_bound.
type_safe::constraints::greater::greater::greater
(1) constexpr greater(const T& bound);
(2) constexpr greater(T&& bound) noexcept('hidden');
Initializes it with a dynamic bound.
Effects: Copies (1)/moves (2) the object and uses that as bound.
Notes: These constructors only participate in overload resolution, if a dynamic bound is used, i.e. Bound
is ts::constraints::dynamic_bound.
type_safe::constraints::greater::operator()
template <typename U>
constexpr bool operator()(const U& u) const;
Does the actual bounds check.
type_safe::constraints::greater::get_bound
constexpr const value_type& get_bound() const noexcept;
Returns: The bound.
type_safe::constraints::greater_equal
template <typename T, typename Bound = dynamic_bound>
class greater_equal
{
public:
using value_type = decltype(base::value);
using bound_type = Bound;
constexpr greater_equal(Bound = {});
constexpr greater_equal(const T& bound);
constexpr greater_equal(T&& bound) noexcept('hidden');
template <typename U>
constexpr bool operator()(const U& u) const;
constexpr const value_type& get_bound() const noexcept;
};
A Constraint
for the ts::constrained_type.
A value is valid if it is greater than or equal to some given value.
type_safe::constraints::greater_equal::greater_equal::greater_equal
constexpr greater_equal(Bound = {});
Initializes it with a static bound.
Effects: Does nothing, a static bound is not stored. It will use Bound::value
as the bound.
Notes: This constructor only participates in overload resolution, if a static bound is used, i.e. Bound
is not ts::constraints::dynamic_bound.
type_safe::constraints::greater_equal::greater_equal::greater_equal
(1) constexpr greater_equal(const T& bound);
(2) constexpr greater_equal(T&& bound) noexcept('hidden');
Initializes it with a dynamic bound.
Effects: Copies (1)/moves (2) the object and uses that as bound.
Notes: These constructors only participate in overload resolution, if a dynamic bound is used, i.e. Bound
is ts::constraints::dynamic_bound.
type_safe::constraints::greater_equal::operator()
template <typename U>
constexpr bool operator()(const U& u) const;
Does the actual bounds check.
type_safe::constraints::greater_equal::get_bound
constexpr const value_type& get_bound() const noexcept;
Returns: The bound.
(1) constexpr bool open = false;
(2) constexpr bool closed = true;
Tag objects to specify bounds for ts::constraints::bounded.
type_safe::constraints::bounded
template <typename T, bool LowerInclusive, bool UpperInclusive, typename LowerBound = dynamic_bound, typename UpperBound = dynamic_bound>
class bounded
{
public:
using value_type = T;
using lower_bound = LowerBound;
using upper_bound = UpperBound;
static constexpr auto lower_inclusive = LowerInclusive;
static constexpr auto upper_inclusive = UpperInclusive;
constexpr bounded();
template <typename U1, typename U2>
constexpr bounded(U1&& lower, U2&& upper, decltype(lower_type(std::forward<U1>(lower)), 0) = 0, decltype(upper_type(std::forward<U2>(upper)), 0) = 0);
template <typename U>
constexpr bool operator()(const U& u) const;
constexpr const typename lower_type::value_type& get_lower_bound() const noexcept;
constexpr const typename upper_type::value_type& get_upper_bound() const noexcept;
};
A Constraint
for the ts::constrained_type.
A value is valid if it is between two given bounds, LowerInclusive
/UpperInclusive
control whether the lower/upper bound itself is valid too.
type_safe::constraints::bounded::bounded::bounded
constexpr bounded();
Initializes it with static bounds.
Effects: Does nothing, a static bound is not stored. It will use LowerBound::value
as lower bound and UpperBound::value
as upper bound.
Notes: This constructor does not participate in overload resolution, unless both bounds are static, i.e. not ts::constraints::dynamic_bound.
type_safe::constraints::bounded::bounded
template <typename U1, typename U2>
constexpr bounded(U1&& lower, U2&& upper, decltype(lower_type(std::forward<U1>(lower)), 0) = 0, decltype(upper_type(std::forward<U2>(upper)), 0) = 0);
Initializes it with (mixed) dynamic bounds.
Effects: Perfectly forwards the arguments to the bounds. If a bound is static, the static member value
will be used as bound, if it is dynamic, a copy created by perfectly forwarding will be stored and used as bound. \notes This constructor does not participate in overload resolution, unless the arguments are convertible to the bounds. \param 2 \exclude \param 3 \exclude
type_safe::constraints::bounded::operator()
template <typename U>
constexpr bool operator()(const U& u) const;
Does the bounds check.
type_safe::constraints::bounded::get_lower_bound
constexpr const typename lower_type::value_type& get_lower_bound() const noexcept;
Returns: The value of the lower bound.
type_safe::constraints::bounded::get_upper_bound
constexpr const typename upper_type::value_type& get_upper_bound() const noexcept;
Returns: The value of the upper bound.
type_safe::constraints::open_interval
template <typename T, typename LowerBound = dynamic_bound, typename UpperBound = dynamic_bound>
using open_interval = bounded<T, open, open, LowerBound, UpperBound>;
A Constraint
for the ts::constrained_type.
A value is valid if it is between two given bounds but not the bounds themselves.
type_safe::constraints::closed_interval
template <typename T, typename LowerBound = dynamic_bound, typename UpperBound = dynamic_bound>
using closed_interval = bounded<T, closed, closed, LowerBound, UpperBound>;
A Constraint
for the ts::constrained_type.
A value is valid if it is between two given bounds or the bounds themselves.
type_safe::bounded_type
template <typename T, bool LowerInclusive, bool UpperInclusive, typename LowerBound = constraints::dynamic_bound, typename UpperBound = constraints::dynamic_bound, typename Verifier = assertion_verifier>
using bounded_type = constrained_type<T, constraints::bounded<T, LowerInclusive, UpperInclusive, LowerBound, UpperBound>, Verifier>;
An alias for ts::constrained_type that uses ts::constraints::bounded as its Constraint
. \notes This is some type where the values must be in a certain interval.
type_safe::literals::operator""_bound
(1) template <char ... Digits>
constexpr 'hidden' operator""_bound();
(2) template <char ... Digits>
constexpr 'hidden' operator""_boundu();
Creates a static bound for ts::bounded_type.
This is a bound encapsulated in the type, so there is no overhead. You can use it for example like this ts::make_bounded(50, 0_bound, 100_bound)
, to bound an integer between 0
and 100
.
Returns: A type representing the given value, the value has type long long
(1)/unsigned long long
(2).
type_safe::make_bounded
template <typename T, typename U1, typename U2>
constexpr 'hidden' make_bounded(T&& value, U1&& lower, U2&& upper);
Creates a ts::bounded_type to a specified ts::constraints::closed_interval.
Returns: A ts::bounded_type with the given value
and lower and upper bounds, where the bounds are valid values as well.
Requires: As it uses ts::assertion_verifier, the value must be valid.
Notes: If this function is passed in dynamic values of the same type as value
, it will create a dynamic bound. Otherwise it must be passed static bounds.
type_safe::sanitize_bounded
template <typename T, typename U1, typename U2>
constexpr 'hidden' sanitize_bounded(T&& value, U1&& lower, U2&& upper);
Creates a ts::bounded_type to a specified ts::constraints::closed_interval.
Returns: A ts::bounded_type with the given value
and lower and upper bounds, where the bounds are valid values as well.
Throws: A ts::constrain_error if the value
isn't valid, or anything else thrown by the constructor.
Notes: This is meant for sanitizing user input, using a recoverable error handling strategy.
Notes: If this function is passed in dynamic values of the same type as value
, it will create a dynamic bound. Otherwise it must be passed static bounds.
type_safe::make_bounded_exclusive
template <typename T, typename U1, typename U2>
constexpr 'hidden' make_bounded_exclusive(T&& value, U1&& lower, U2&& upper);
Creates a ts::bounded_type to a specified ts::constraints::open_interval.
Returns: A ts::bounded_type with the given value
and lower and upper bounds, where the bounds are not valid values.
Requires: As it uses ts::assertion_verifier, the value must be valid.
Notes: If this function is passed in dynamic values of the same type as value
, it will create a dynamic bound. Otherwise it must be passed static bounds.
type_safe::sanitize_bounded_exclusive
template <typename T, typename U1, typename U2>
constexpr 'hidden' sanitize_bounded_exclusive(T&& value, U1&& lower, U2&& upper);
Creates a ts::bounded_type to a specified ts::constraints::open_interval, using ts::throwing_verifier.
Returns: A ts::bounded_type with the given value
and lower and upper bounds, where the bounds are not valid values.
Throws: A ts::constrain_error if the value
isn't valid, or anything else thrown by the constructor.
Notes: This is meant for sanitizing user input, using a recoverable error handling strategy.
Notes: If this function is passed in dynamic values of the same type as value
, it will create a dynamic bound. Otherwise it must be passed static bounds.
type_safe::clamp
template <typename T, typename LowerBound, typename UpperBound, typename U>
constexpr typename std::decay<U>::type clamp(const constraints::closed_interval<T, LowerBound, UpperBound>& interval, U&& val);
Returns a copy of val
so that it is in the given ts::constraints::closed_interval.
Effects: If it is not in the interval, returns the bound that is closer to the value.
type_safe::clamping_verifier
struct clamping_verifier
{
template <typename Value, typename T, typename Bound>
static constexpr typename std::decay<Value>::type verify(Value&& val, const constraints::less_equal<T, Bound>& p);
template <typename Value, typename T, typename Bound>
static constexpr typename std::decay<Value>::type verify(Value&& val, const constraints::greater_equal<T, Bound>& p);
template <typename Value, typename T, typename LowerBound, typename UpperBound>
static constexpr typename std::decay<Value>::type verify(Value&& val, const constraints::closed_interval<T, LowerBound, UpperBound>& interval);
};
A Verifier
for ts::constrained_type that clamps the value to make it valid.
It must be used together with ts::constraints::less_equal, ts::constraints::greater_equal or ts::constraints::closed_interval.
type_safe::clamping_verifier::verify
template <typename Value, typename T, typename Bound>
static constexpr typename std::decay<Value>::type verify(Value&& val, const constraints::less_equal<T, Bound>& p);
Returns: If val
is greater than the bound of p
, returns the bound. Otherwise returns the value unchanged
type_safe::clamping_verifier::verify
template <typename Value, typename T, typename Bound>
static constexpr typename std::decay<Value>::type verify(Value&& val, const constraints::greater_equal<T, Bound>& p);
Returns: If val
is less than the bound of p
, returns the bound. Otherwise returns the value unchanged
type_safe::clamping_verifier::verify
template <typename Value, typename T, typename LowerBound, typename UpperBound>
static constexpr typename std::decay<Value>::type verify(Value&& val, const constraints::closed_interval<T, LowerBound, UpperBound>& interval);
Returns: Same as clamp(interval, val)
.
type_safe::clamped_type
template <typename T, typename LowerBound = constraints::dynamic_bound, typename UpperBound = constraints::dynamic_bound>
using clamped_type = constrained_type<T, constraints::closed_interval<T, LowerBound, UpperBound>, clamping_verifier>;
An alias for ts::constrained_type that uses ts::constraints::closed_interval as its Constraint
and ts::clamping_verifier as its Verifier
. \notes This is some type where the values are always clamped so that they are in a certain interval.
type_safe::make_clamped
template <typename T, typename U1, typename U2>
constexpr 'hidden' make_clamped(T&& value, U1&& lower, U2&& upper);
Creates a ts::clamped_type from the specified ts::constraints::closed_interval.
Returns: A ts::clamped_type with the given value
and lower and upper bounds, where the bounds are valid values.
Notes: If this function is passed in dynamic values of the same type as value
, it will create a dynamic bound. Otherwise it must be passed static bounds.